#pragma once
#include <pthread.h>
#include <unordered_map>
#include "cloud_config.hpp"

namespace cloud
{
  struct BackupInfo
  {
    bool pack_flag; //压缩标志
    time_t mtime; //最后修改时间
    time_t atime; //最后访问时间 
    size_t fsize; //文件大小
    std::string real_path; //文件存储路径 -> ./back_dir/xxx.xx
    std::string url; //下载路径 -> /download/xxx.xx
    std::string pack_path; //压缩路径 -> ./pack_dir/xxx.xx.lz
    bool InitBackupInfo(const std::string& file_name)
    {
      FileUtil fu(file_name);
      //如果该文件不存在
      if(fu.Exists() == false)
      {
        std::cout << "InitBackupInfo() error, the file does not exist." << std::endl;
        return false;
      }
      //初始化成员变量
      pack_flag = false;
      mtime = fu.LastMTime();
      atime = fu.LastATime();
      fsize = fu.FileSize();
      Config* conf = Config::_get_instance(); //获取单例对象
      real_path = conf->GetBackDir() + fu.FileName();
      url = conf->GetURLPrefix() + fu.FileName();
      pack_path = conf->GetPackDir() + fu.FileName() + conf->GetArcSuffix();
      return true;
    }
  };

  class DataManager
  {
  private:
    std::string _backup_dir;
    pthread_rwlock_t _rwlock; //读写锁
    std::unordered_map<std::string, BackupInfo> _table; //key--url val--BackupInfo
  public:
    //默认构造
    DataManager()
    {
      _backup_dir = Config::_get_instance()->GetManagerFile();
      pthread_rwlock_init(&_rwlock, nullptr); //初始化读写锁
      InitLoad(); //构造时加载备份文件
    }
    
    //析构
    ~DataManager()
    {
      pthread_rwlock_destroy(&_rwlock); //销毁读写锁  
    }
    
    //向_table插入数据
    bool Insert(const BackupInfo& info)
    {
      pthread_rwlock_wrlock(&_rwlock); //加锁
      _table[info.url] = info;
      pthread_rwlock_unlock(&_rwlock); //解锁
      Storage(); //在插入完成后备份
      return true;
    }
    
    //更新_table[info.url]的数据
    bool Updata(const BackupInfo& info)
    {
      pthread_rwlock_wrlock(&_rwlock); //加锁
      _table[info.url] = info;
      pthread_rwlock_unlock(&_rwlock); //解锁
      Storage(); //在更新完成后备份
      return true;
    }
    
    //获取指定url的BackupInfo
    bool GetOneByURL(const std::string& url, BackupInfo& info)
    {
      pthread_rwlock_wrlock(&_rwlock);
      auto it = _table.find(url);
      //找不到该url对应的BackupInfo
      if(it == _table.end())
      {
        std::cout << "GetOneByURL() error, Unable to find the BackupInfo corresponding to this URL." << std::endl;
        pthread_rwlock_unlock(&_rwlock);
        return false;
      }
      info = it->second; //找到了
      return true;
    }
    
    //获取指定real_path的BackupInfo
    bool GetOneByRealPath(const std::string& real_path, BackupInfo& info)
    {
      pthread_rwlock_wrlock(&_rwlock);
      auto it = _table.begin();
      while(it != _table.end())
      {
        if(real_path == it->second.real_path)
        {
          info = it->second;
          pthread_rwlock_unlock(&_rwlock);
   		    return true;
        }
				++it;
      }
			pthread_rwlock_unlock(&_rwlock);
			return false;
    }
    
    //获取DataManager的全部数据
    bool GetAll(std::vector<BackupInfo>& arr)
    {
			pthread_rwlock_wrlock(&_rwlock);
      auto it = _table.begin();
      while(it != _table.end())
      {
        arr.push_back(it->second);
        ++it;
      } 
      pthread_rwlock_unlock(&_rwlock);
      return true;
    }

    //备份数据到文件
    bool Storage()
    {
      //1.获取DataManager中的全部数据
      std::vector<BackupInfo> arr;
      GetAll(arr);
      //2.将arr中的数据传入root中,root存储的对象是Json::Value
      Json::Value root;
      for(const auto& e : arr)
      {
        Json::Value item;
        item["pack_flag"] = e.pack_flag;
        item["mtime"] = (Json::Int64)e.mtime;
        item["atime"] = (Json::Int64)e.atime;
        item["fsize"] = (Json::Int64)e.fsize;
        item["real_path"] = e.real_path;
        item["url"] = e.url;
        item["pack_path"] = e.pack_path;

        root.append(item);
      }
      //3.将root序列化,然后写入备份路径
      std::string body;
      JsonUtil::Serialize(root, body);
      FileUtil fu(_backup_dir);
      fu.SetFileContent(body);
      return true;
    }

    //初始化时加载文件
    bool InitLoad()
    {
      FileUtil fu(_backup_dir);
      //不存在备份文件
      if(fu.Exists() == false)
      {
        std::cout << "InitLoad() nothing to do." << std::endl;
        return true;
      }
      std::string body;
      fu.GetFileContent(body); //将序列化内容写入body
      Json::Value root;
      JsonUtil::UnSerialize(body, root); //将反序列化的内容写入root
      //将备份文件中的数据写入_table
      for(int i = 0; i < root.size(); ++i)
      {
        BackupInfo info;
        info.atime = root[i]["atime"].asInt64();
        info.mtime = root[i]["mtime"].asInt64();
        info.fsize = root[i]["fsize"].asInt64();
        info.pack_flag = root[i]["pack_flag"].asBool();
        info.url = root[i]["url"].asString();
        info.pack_path = root[i]["pack_path"].asString();
        info.real_path = root[i]["real_path"].asString();
        Insert(info);
      }
      return true;
    }
  };
}
